﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using Spring.Objects.Factory.Config;
using Strongshell.Recoil.Core.Composition.Parsing;

namespace Strongshell.Recoil.Core.Composition.Workflow
{
    public class DefinitionFragment<TTarget> : IDefinitionFragment<TTarget>
    {
        private readonly FragmentContext context;

        public DefinitionFragment(FragmentContext context)
        {
            this.context = context;
        }

        public IDefinitionFragment<TTarget> As<TInterface>()
        {
            context.ObjectConfiguration.Name = context.NameFactory.CreateName<TInterface>();
            return this;
        }

        public IDefinitionFragment<TTarget> As(string name)
        {
            context.ObjectConfiguration.Name = name;
            return this;
        }

        public IDefinitionFragment<TTarget> Alias<TInterface>()
        {
            context.ObjectConfiguration.Aliases.Add(context.NameFactory.CreateName<TInterface>());
            return this;
        }

        public IDefinitionFragment<TTarget> Alias(string name)
        {
            context.ObjectConfiguration.Aliases.Add(name);
            return this;
        }

        public IDefinitionFragment<TTarget> AsSingleton()
        {
            context.ObjectConfiguration.Builder.SetSingleton(true);
            return this;
        }

        public IDefinitionFragment<TTarget> AsPrototype()
        {
            context.ObjectConfiguration.Builder.SetSingleton(false);
            return this;
        }

        public IDefinitionFragment<TTarget> LazyInit(bool enable)
        {
            context.ObjectConfiguration.Builder.SetLazyInit(enable);
            return this;
        }

        public IDefinitionFragment<TTarget> AutoWire(AutoWiringMode wiringMode)
        {
            context.ObjectConfiguration.Builder.SetAutowireMode(wiringMode);
            return this;
        }

        public IMappingFragment<TTarget, TProperty> Set<TProperty>(Expression<Func<TTarget, TProperty>> selector)
        {
            var body = selector.Body as MemberExpression;

            if (body == null)
            {
                throw new ArgumentException("Selector must select a field or a property");
            }

            if (IsMemberTypeAllowedForWire(body.Member.MemberType) == false)
            {
                throw new ArgumentException("Selector must select a field or a property");
            }

            return new MappingFragment<TTarget, TProperty>(context, body.Member.Name);
        }

        public IDefinitionFragment<TTarget> Init(Expression<Action<TTarget>> selector)
        {
            var callExpression = selector.Body as MethodCallExpression;

            if (callExpression == null)
            {
                throw new ArgumentException("Selector must select a method");
            }

            context.ObjectConfiguration.Builder.SetInitMethodName(callExpression.Method.Name);

            return this;
        }

        private static bool IsMemberTypeAllowedForWire(MemberTypes memberType)
        {
            return memberType == MemberTypes.Field || memberType == MemberTypes.Property;
        }

        public string Name
        {
            get { return context.ObjectConfiguration.Name; }
        }
    }
}